package back;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;

/**
 * Clase que se encarga de la transferencia de informacion entre el juego
 * y archivos de almacenamiento, tanto de partidas guardadas, como de
 * niveles existentes.
 */

public class Archivos {

	/**
	 * Se encarga de retornar la proxima linea con informacion relevante.
	 * 
	 * @param BufferedReader archivo archivo del cual se van a levantar los valores.
	 * @return String la linea con la informacion.
	 * @throws IOException si hubiera error en la lectura.
	 */
	
	static final String lineaParseable(BufferedReader archivo) throws IOException {
		String linea = "#";
		do {
			linea.replaceAll("\\s+", "");
			if(!(linea.length() == 0) && linea.charAt(0) != '#') {
				break;
			}
			linea = archivo.readLine();
		} while(linea != null);
		return linea;
	}
	
	/**
	 * Se encarga de guardar en levels los archivos que son niveles
	 * 
	 * @param levels
	 * @param File[] archivos
	 * @throws IOException si hubiera error en la lectura.
	 */
	
	public static void seleccionaNiveles(Set<File> levels, File[] archivos) throws IOException {
		for(int i=0; i < archivos.length; i++){
			if(!(archivos[i].isDirectory() || archivos[i].getAbsolutePath().contains(".scores")))
				levels.add(archivos[i]);
			
		}
		
	}
	
	/**
	 * Se encarga de parsear la linea y dividirla en los valores.
	 * 
	 * @param String linea string con los enteros a parsear.
	 * @return int[] array de enteros parseados de la linea recibida.
	 * @throws IOException si la linea no tuviera la cantidad esperada
	 * de argumentos.
	 * 
	 */
	
	static final int[] atoi(String linea) throws IOException {
		linea = linea.split("#")[0];
		String[] valores = linea.split(",");
		if(valores.length > 7) {
			throw new IOException();
		}
		int[] retorno = new int[valores.length];
		for(int cant = 0; cant < valores.length; cant++) {
			retorno[cant] = Integer.parseInt(valores[cant].replaceAll("\\s+", ""));
		}
		return retorno;
	}
	
	/**
	 * Se encarga de crear un objeto, segun cual sea
	 * el entero que describe al mismo en el archivo.
	 * Ademas es la responsable de validar que los datos sean validos.
	 * 
	 * @param int[] datos array con los enteros levantados de la linea del archivo.
	 * @return Object instancia de la clase Componente.
	 */
	
	static final Object leerCelda(int[] datos) {
		Object retorno = null;
		switch(datos[2]) {
		case(1):	if(datos[3] == 0 && datos[4] == 0 && datos[5] == 0 && datos[6] == 0){
							retorno= new Jugador(null);
							break;
					}
		
					throw new IllegalArgumentException();
		case(2):	
						if(datos[4] <= 255 && datos[5] <= 255 && datos[6] <= 255) {
								if(datos[4] >= 0 && datos[5] >= 0 && datos[6] >= 0) {
										if(datos[3] == 0) {
											retorno = new Caja(new Color(datos[4], datos[5], datos[6]));
											break;
										}	}	}
					throw new IllegalArgumentException();
			
			case(3):	
					if(datos[4] <= 255 && datos[5] <= 255 && datos[6] <= 255) {
						if(datos[4] >= 0 && datos[5] >= 0 && datos[6] >= 0) {
							if(datos[3] == 0) {
								retorno = new Destino(new Color(datos[4], datos[5], datos[6]));
								break;
					}	}	}
				throw new IllegalArgumentException();
			case(4): 
						if(datos[3] == 0 && datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
								retorno = new Pared();
								break;
							
						}
						throw new IllegalArgumentException();
			case(5): 
						if(datos[3] == 0 && datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
							retorno = new Agujero();
							break;
							}
						throw new IllegalArgumentException();
			case(6): 
						if(datos[4] == 0 && datos[5] == 0 && datos[6] == 0) {
							retorno = new Camino(datos[3]);
							break;
							
						}
						throw new IllegalArgumentException();
			
			default: 
						throw new IllegalArgumentException();
		}
		return retorno;
	}
	
	/**
	 * Se encarga de cargar el juego con la informacion que levanta del archivo.
	 * 
	 * @param Juego juego parametro de entrada salida en el cual se guardara la informacion
	 * obtenida del archivo.
	 * @param File archivo con la informacion a levantar.
	 * @throws IOException si hubiera error en la lectura.
	 */
	
	public static void cargaJuego(Juego juego, File archivo) throws IOException{
		BufferedReader nivel=null, aux=null;
		nivel= new BufferedReader(new FileReader(archivo));
		aux= new BufferedReader(new FileReader(archivo));
		String jugador = lineaParseable(nivel);
		int movimientos = Integer.valueOf(lineaParseable(nivel)).intValue();
		File[] niveles = cargarNiveles();
		String nombreNivel = lineaParseable(nivel);
		for(int i=0; i< niveles.length; i++){
			if(niveles[i].getName().contains(nombreNivel+".")){				
				juego.setNivelActual(i);
			}	
		}
		lineaParseable(aux);
		lineaParseable(aux);
		cargarTablero(juego,jugador,aux, movimientos);
	}
	
	/**
	 * Se encarga de crear un File[] con los niveles y lo retorna
	 * @throws IOException si hubiera error en la lectura.
	 */
	
	public static File[] cargarNiveles() throws IOException{
	File directorioLevels=new File("levels");
	Set<File> levels=new TreeSet<File>();
	File[] archivos= directorioLevels.listFiles();
	seleccionaNiveles(levels, archivos);
	File[] niveles= new File[levels.size()];
	int i=0;
	for(File f:levels){
		niveles[i++]=f;
	}
	return niveles;
	}
	
	/**
	 * Se encarga de cargar el tablero con los objetos correspondientes
	 * @param Juego juego
	 * @param String jugador nombre del jugador
	 * @param BufferedReader nivel
	 * @param int movimientos hechos por el jugador
	 * @throws IOException si hubiera error en la lectura.
	 */
		
	public static void cargarTablero(Juego juego, String jugador,BufferedReader nivel, int movimientos) throws IOException{
		
		juego.cambiaNivel(lineaParseable(nivel));
		juego.cambiaJugador(jugador);
		int[] vect= atoi(lineaParseable(nivel));
		Tablero tablero= new Tablero(vect[0], vect[1]);
		
		
		while(true){
			try{
				vect= atoi(lineaParseable(nivel));
				Object celda= leerCelda(vect);
				tablero.agrega(celda, new Coordenada(vect[0], vect[1]));
			}
			catch(NullPointerException e){
				break;
			}
		}

		tablero.setMovimientos(movimientos);
		juego.cambiaTablero(tablero);
		nivel.close();
	}

	/**
	 * Se encarga de guardar en un archivo el juego que se esta ejecutando.
	 * 
	 * @param Juego juego parametro de entrada del cual se obtendra la informacion
	 * que sera salvada en el archivo.
	 * @param File archivo en el que se guardara la informacion.
	 * @throws IOException si hubiera error en la escritura.
	 */

	public static void guardarArchivo(Juego juego, File archivo) throws IOException {
	if(archivo.exists() == false) {
		archivo.createNewFile();
	}
	BufferedWriter file = new BufferedWriter(new FileWriter(archivo));
	
	Tablero tablero = juego.getTablero();
	int filas = tablero.getFils();
	int columnas = tablero.getCols();
	
	String linea = null;
	
	file.write(juego.verJugador());
	file.newLine();
	file.write(String.valueOf(juego.getTablero().getMovimientos()));
	file.newLine();
	file.write(juego.verNivel());
	file.newLine();
	file.write(filas + "," + columnas);
	file.newLine();
	file.newLine();
	
	try {
		file.write("#Jugador");
		file.newLine();
		for(int i = 0; i < filas; i++) {
			for(int j = 0; j < columnas; j++) {
				Coordenada posicion = new Coordenada(i,j);
				if(tablero.getCelda(posicion).getMovible().getClass() == Jugador.class){
					linea = i + ","+ j + ",1,0,0,0,0";
					file.write(linea);
					file.newLine();
				}
			}	
		}
		file.write("#Paredes");
		file.newLine();
		for(int i = 0; i < filas; i++) {
			for(int j = 0; j < columnas; j++) {
				Coordenada posicion = new Coordenada(i,j);
				if(tablero.getCelda(posicion).getNoMovible().getClass() == Pared.class){
					linea = i + ","+ j + ",4,0,0,0,0";
					file.write(linea);
					file.newLine();
				}
			}	
		}
		file.write("#Cajas");
		file.newLine();
		for(int i = 0; i < filas; i++) {
			for(int j = 0; j < columnas; j++) {
				Coordenada posicion = new Coordenada(i,j);
				if(tablero.getCelda(posicion).getMovible().getClass() == Caja.class){
					Caja c=(Caja) tablero.getCelda(posicion).getMovible();
					linea = i + ","+ j + ",2,0,"
					+c.getColor().getRed()+","+c.getColor().getGreen()+","+c.getColor().getBlue();
					file.write(linea);
					file.newLine();
				}
			}	
		}
		file.write("#Destino");
		file.newLine();
		for(int i = 0; i < filas; i++) {
			for(int j = 0; j < columnas; j++) {
				Coordenada posicion = new Coordenada(i,j);
				if(tablero.getCelda(posicion).getNoMovible().getClass() == Destino.class){
					Destino d=(Destino) tablero.getCelda(posicion).getNoMovible();
					linea = i + ","+ j + ",3,0,"
					+d.getColor().getRed()+","+d.getColor().getGreen()+","+d.getColor().getBlue();
					file.write(linea);
					file.newLine();
				}
			}	
		}
		file.write("#Agujero");
		file.newLine();
		for(int i = 0; i < filas; i++) {
			for(int j = 0; j < columnas; j++) {
				Coordenada posicion = new Coordenada(i,j);
				if(tablero.getCelda(posicion).getNoMovible().getClass() == Agujero.class){
					linea = i + ","+ j + ",5,0,0,0,0";
					file.write(linea);
					file.newLine();
				}
			}	
		}
		file.write("#Camino");
		file.newLine();
		for(int i = 0; i < filas; i++) {
			for(int j = 0; j < columnas; j++) {
				Coordenada posicion = new Coordenada(i,j);
				if(tablero.getCelda(posicion).getNoMovible().getClass() == Camino.class){
					Camino cam=(Camino) tablero.getCelda(posicion).getNoMovible();
					linea = i + ","+ j + ",6,"+cam.getDireccion()+",0,0,0";
					file.write(linea);
					file.newLine();
				}
			}	
		}
	} finally {
		if(file != null) {
			file.close();
		}
	}
}

	/**
	 * Se encarga de retornar el archivo con los puntajes del nivel
	 * @param String text numero de nivel
	 * @throws IOException 
	 */

	public static File BuscaPuntajeNivel(String text) throws IOException{
		File directorioLevels=new File("levels");
		File[] archivos= directorioLevels.listFiles();
		Set<File> puntajesAux=new TreeSet<File>();
		seleccionaPuntajes(puntajesAux, archivos);
		
		File[] puntajes= new File[puntajesAux.size()];
		int i=0;
		for(File f:puntajesAux){
			puntajes[i++]=f;
		}
		
		File resp = null;
		i = Integer.valueOf(text) -1;
		System.out.println(i);
		System.out.println(puntajes.length);
		if(puntajes.length >= i){
			resp=puntajes[i];
		}
		
		return resp;
	}

	private static void seleccionaPuntajes(Set<File> puntajes, File[] archivos) throws IOException {
		for(int i=0; i < archivos.length; i++){
			if(archivos[i].getAbsolutePath().contains(".scores"))
				puntajes.add(archivos[i]);
				
		}
		
	}
}


